﻿using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;
using System.ComponentModel;

namespace DatabaseGroup.SSIS.SourceAdapters.View
{
    /// <summary>
    /// Main UI for the component, deals with all aspects
    /// of the components configuration
    /// </summary>
    public partial class EditorView : Form, 
        IEditorGeneralView, IEditorColumnView, IEditorAdvanceView, IEditorErrorView,
        IEditorAuditView, IEditorPreviewView, IEditorMainView
    {
        #region IEditor*View events

        /// <summary>
        /// Indicates the user has clicked the OK button and desires
        /// the settings to be saved
        /// </summary>
        public event CancelEventHandler SavingSettings;

        /// <summary>
        /// Indicates the user has clicked the OK button and all
        /// settings have been successfully saved
        /// </summary>
        public event EventHandler SavedSettings;

        /// <summary>
        /// Indicates the user has clicked the cancel button and
        /// desires the changes to be discarded
        /// </summary>
        public event EventHandler CancelSettings;

        /// <summary>
        /// Indicates that a tab is being navigated away from
        /// and gives the option to cancel the event
        /// </summary>
        public event CancelTabEventHandler LeavingTab;

        /// <summary>
        /// Indicates a tab has been selected for viewing
        /// </summary>
        public event TabEventHandler ViewSettings;

        /// <summary>
        /// Indicates the selected file connection has been changed
        /// </summary>
        public event EventHandler FileConnectionChange;

        /// <summary>
        /// Indicates that one of the row/column delimiters has been changed
        /// </summary>
        public event EventHandler MainDelimitersChanged;

        /// <summary>
        /// Indicates the user wishes to reset the column configuration
        /// based on the current component/source configuration
        /// </summary>
        public event EventHandler ResetColumns;

        /// <summary>
        /// Indicates the user has requested a new standard output column to be added
        /// </summary>
        public event EventHandler AddNewAdvanceColumn;

        /// <summary>
        /// Indicates the user has requested a new standard output column to be added
        /// and placed before the currently selected column
        /// </summary>
        public event IndexEventHandler AddNewAdvanceColumnBefore;

        /// <summary>
        /// Indicates the user has requested a new standard output column to be added
        /// and placed after the currently selected column
        /// </summary>
        public event IndexEventHandler AddNewAdvanceColumnAfter;

        /// <summary>
        /// Indicates the user has requested to remove the
        /// currently selected standard output column
        /// </summary>
        public event IndexEventHandler RemoveAdvanceColumn;
         
        /// <summary>
        /// Indicates the currently selected standard output column on the
        /// advance tab has been changed
        /// </summary>
        public event IndexEventHandler SelectedAdvanceColumnChanged;

        /// <summary>
        /// Indicates the log source datatype has been updated
        /// </summary>
        public event EventHandler LogSourceDataTypeUpdated;

        /// <summary>
        /// Indicates the error source datatype has been updated
        /// </summary>
        public event EventHandler ErrorSourceDataTypeUpdated;

        /// <summary>
        /// Indicates the preview rows should be refreshed with the
        /// current settings
        /// </summary>
        public event EventHandler RefreshPreview;

        #endregion

        private int _flowLayoutWidthPadding;
        private int _errorBoxPanelHeight;
        private int _bottomPadding;
        private bool _formIsMaximized;

        private readonly Dictionary<EditorViewTab, ITabSettings> _settings = new Dictionary<EditorViewTab, ITabSettings>();

        /// <summary>
        /// Initializes a new instance of the EditorView class
        /// and prepares the layout for use
        /// </summary>
        public EditorView()
        {
            InitializeComponent();
            SaveStaticFormMeasurements();
            HeightResizeMainPanelToFit();
            HideErrorMessage();
            InitializeHiddenTabButtons();
            AssociateTabs();
        }

        #region IEditor*View implementations

        /// <summary>
        /// Sets the DialogResult and then closes the form
        /// </summary>
        /// <param name="dialogResultIsOK">True if the dialog result should be set to OK</param>
        public void CloseView(bool dialogResultIsOK)
        {
            this.DialogResult = dialogResultIsOK ? DialogResult.OK : DialogResult.Cancel;
            Close();
        }

        /// <summary>
        /// Displays a messagebox to the user requesting a yes/no response
        /// </summary>
        /// <param name="message">The confirmation message to display</param>
        /// <returns>True if the user confirms (OKs) the message</returns>
        public bool RequestUserConfirmation(string message)
        {
            // TODO: MessageStrings
            if (MessageBox.Show(this, message, "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                return true;

            return false;
        }

        /// <summary>
        /// Displays a messagebox alert to the user with a single OK button
        /// </summary>
        /// <param name="message">The message to display</param>
        public void ShowAlert(string message)
        {
            MessageBox.Show(this, message, "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }

        /// <summary>
        /// Displays an error message within the view
        /// </summary>
        /// <param name="message">The error message to display</param>
        public void ShowErrorMessage(string message)
        {
            ToggleErrorMessage(true, message);
        }

        /// <summary>
        /// Hides any displayed error messages
        /// </summary>
        public void HideErrorMessage()
        {
            ToggleErrorMessage(false, null);
        }

        /// <summary>
        /// Returns the settings of a tab based on the controls hosted within it
        /// </summary>
        /// <typeparam name="T">The concrete type of the ITabSettings required</typeparam>
        /// <param name="tab">The tab to retrieve the settings for</param>
        /// <returns>The ITabSettings object</returns>
        public T GetTabSettings<T>(EditorViewTab tab) where T : ITabSettings
        {
            return (T)_settings[tab];
        }

        /// <summary>
        /// Populates the list of preview rows with column data
        /// </summary>
        /// <param name="headers">The column headers to use</param>
        /// <param name="rows">The list of rows containing the column data</param>
        public void PopulateColumnPreviewRows(List<string> headers, List<string[]> rows)
        {
            PopulateListView(lvColumnPreviewRows, headers, rows);
        }

        /// <summary>
        /// Populates the list of preview rows with column data
        /// </summary>
        /// <param name="headers">The column headers to use</param>
        /// <param name="rows">The list of rows containing the column data</param>
        public void PopulatePreviewRows(List<string> headers, List<string[]> rows)
        {
            llRefreshPreviewRows.Visible = false;
            PopulateListView(lvPreviewRows, headers, rows);
            lvPreviewRows.Enabled = true;
        }

        /// <summary>
        /// Populates the file connection list
        /// </summary>
        /// <param name="connections">The list of file connections</param>
        /// <param name="selected">The file connection that should be selected</param>
        public void PopulateFileConnections(List<string> connections, string selected)
        {
            cmbConnectionManager.Items.Clear();

            for (int index = 0; index < connections.Count; index++)
            {
                cmbConnectionManager.Items.Add(connections[index]);

                if (connections[index] == selected)
                {
                    cmbConnectionManager.SelectedIndexChanged -= cmbConnectionManager_SelectedIndexChanged;
                    cmbConnectionManager.SelectedIndex = index;
                    cmbConnectionManager.SelectedIndexChanged += cmbConnectionManager_SelectedIndexChanged;
                }
            }

            cmbConnectionManager.Items.Add(MessageStrings.NewFileConnection);
            cmbConnectionManager.DataBindings.Clear();
            cmbConnectionManager.DataBindings.Add("SelectedItem", _settings[EditorViewTab.General], "FileConnection");
        }

        /// <summary>
        /// Changes the availability for the error source size
        /// </summary>
        /// <param name="inputIsAvailable">True if the input should be made available to the user</param>
        public void SetErrorSourceSizeAvailability(bool inputIsAvailable)
        {
            nudErrorSourceSize.Enabled = inputIsAvailable;
        }

        /// <summary>
        /// Changes the availability for the log source size
        /// </summary>
        /// <param name="inputIsAvailable">True if the input should be made available to the user</param>
        public void SetLogSourceSizeAvailability(bool inputIsAvailable)
        {
            nudLogSourceSize.Enabled = inputIsAvailable;
        }

        /// <summary>
        /// Binds the controls contained on the general tab to the provided settings object
        /// </summary>
        /// <param name="settings">The values to apply to the controls</param>
        public void BindGeneralTab(GeneralTabSettings settings)
        {
            ClearGeneralTabBindings();
            cmbConnectionManager.DataBindings.Add("SelectedItem", settings, "FileConnection");
            txtName.DataBindings.Add("Text", settings, "ComponentName");
            txtDescription.DataBindings.Add("Text", settings, "ComponentDescription");
            cmbLocale.DataBindings.Add("SelectedItem", settings, "Locale");
            cmbCodePage.DataBindings.Add("SelectedItem", settings, "CodePage");
            chkIsUnicode.DataBindings.Add("Checked", settings, "IsUnicode");
            txtTextQualifier.DataBindings.Add("Text", settings, "TextQualifier");
            cmbHeaderRowDelimiter.DataBindings.Add("Text", settings, "HeaderRowDelimiter");
            nudHeaderRowsToSkip.DataBindings.Add("Value", settings, "HeaderRowsToSkip");
            nudDataRowsToSkip.DataBindings.Add("Value", settings, "DataRowsToSkip");
            txtEscapeChar.DataBindings.Add("Text", settings, "EscapeChar");
            chkColumnNamesAreInFirstDataRow.DataBindings.Add("Checked", settings, "ColumnNamesInFirstDataRow");
            chkTrimWhitespace.DataBindings.Add("Checked", settings, "TrimWhiteSpace");
            chkTreatEmptyStringsAsNull.DataBindings.Add("Checked", settings, "TreatEmptyStringsAsNull");

            _settings[EditorViewTab.General] = settings;
        }

        /// <summary>
        /// Binds the controls contained on the column tab to the provided settings object
        /// </summary>
        /// <param name="settings">The values to apply to the controls</param>
        public void BindColumnTab(ColumnTabSettings settings)
        {
            ClearColumnTabBindings();
            cmbRowDelimiter.DataBindings.Add("Text", settings, "RowDelimiter");
            cmbColumnDelimiter.DataBindings.Add("Text", settings, "ColumnDelimiter");

            _settings[EditorViewTab.Columns] = settings;
        }

        /// <summary>
        /// Binds the controls contained on the advance tab to the provided settings object
        /// </summary>
        /// <param name="settings">The values to apply to the controls</param>
        public void BindAdvanceTab(AdvanceTabSettings settings)
        {
            int selectedIndex = bcblColumns.SelectedIndex;

            bcblColumns.BeginUpdate();
            bcblColumns.DisplayMember = "Name";
            bcblColumns.CheckedMember = "IsForOutput";
            bcblColumns.DataSource = settings.Columns;
            bcblColumns.EndUpdate();

            if (selectedIndex >= 0 && selectedIndex < settings.Columns.Count)
            {
                bcblColumns.SelectedIndexChanged -= bcblColumns_SelectedIndexChanged;
                bcblColumns.SelectedIndex = selectedIndex;
                bcblColumns.SelectedIndexChanged += bcblColumns_SelectedIndexChanged;
            }

            _settings[EditorViewTab.Advance] = settings;
        }

        /// <summary>
        /// Binds the controls contained on the error tab to the provided settings object
        /// </summary>
        /// <param name="settings">The values to apply to the controls</param>
        public void BindErrorTab(ErrorTabSettings settings)
        {
            ClearErrorTabBindings();
            cmbOutputTruncationAction.DataBindings.Add("SelectedItem", settings, "ComponentTruncationHandling");
            cmbOutputErrorAction.DataBindings.Add("SelectedItem", settings, "ComponentErrorHandling");
            cmbErrorAction.DataBindings.Add("SelectedItem", settings, "ApplyHandling");
            dgvErrorOutput.AutoGenerateColumns = false;
            dgvErrorOutput.DataSource = settings.Columns;
            
            _settings[EditorViewTab.Error] = settings;
        }

        /// <summary>
        /// Binds the controls contained on the audit tab to the provided settings object
        /// </summary>
        /// <param name="settings">The values to apply to the controls</param>
        public void BindAuditTab(AuditTabSettings settings)
        {
            ClearAuditTabBindings();
            chkIncludeLogOutput.DataBindings.Add("Checked", settings, "IncludeLogOutput");
            chkIncludeLogValidationMessages.DataBindings.Add("Checked", settings, "IncludeLogValidationMessages");
            chkIncludeLogSourceData.DataBindings.Add("Checked", settings, "IncludeLogSourceData");
            cmbLogSourceDatatype.DataBindings.Add("SelectedItem", settings, "LogSourceDataType");
            nudLogSourceSize.DataBindings.Add("Value", settings, "LogSourceDataSize");

            chkIncludeErrorOutputAuditColumns.DataBindings.Add("Checked", settings, "IncludeErrorAuditing");
            chkIncludeErrorValidationMessages.DataBindings.Add("Checked", settings, "IncludeErrorValidationMessages");
            chkIncludeErrorSourceData.DataBindings.Add("Checked", settings, "IncludeErrorSourceData");
            cmbErrorSourceDataType.DataBindings.Add("SelectedItem", settings, "ErrorSourceDataType");
            nudErrorSourceSize.DataBindings.Add("Value", settings, "ErrorSourceDataSize");

            _settings[EditorViewTab.Auditing] = settings;
        }

        /// <summary>
        /// Binds the controls contained on the preview tab to the provided settings object
        /// </summary>
        /// <param name="settings">The values to apply to the controls</param>
        public void BindPreviewTab(PreviewTabSettings settings)
        {
            ClearPreviewTabBindings();
            nudPreviewDataRowsToSkip.DataBindings.Add("Value", settings, "DataRowsToSkip");

            _settings[EditorViewTab.Preview] = settings;
        }

        /// <summary>
        /// Populates the Code Page list with an array of items
        /// </summary>
        /// <param name="items">The array of items to populate the list with</param>
        public void PopulateCodePageList(string[] items)
        {
            cmbCodePage.BeginUpdate();
            cmbCodePage.Items.Clear();
            cmbCodePage.Items.AddRange(items);
            cmbCodePage.EndUpdate();
        }

        /// <summary>
        /// Populates the Locale list with an array of items
        /// </summary>
        /// <param name="items">The array of items to populate the list with</param>
        public void PopulateLocaleList(string[] items)
        {
            cmbLocale.BeginUpdate();
            cmbLocale.Items.Clear();
            cmbLocale.Items.AddRange(items);
            cmbLocale.EndUpdate();
        }

        /// <summary>
        /// Sets the selected object of the property grid
        /// </summary>
        /// <param name="columnIndex">The column index to select in the advance column list</param>
        /// <param name="propertiesObject">The properties to be examined within the property grid</param>
        public void ViewAdvanceColumnProperties(int columnIndex, object propertiesObject)
        {
            bcblColumns.SelectedIndexChanged -= bcblColumns_SelectedIndexChanged;
            bcblColumns.SelectedIndex = columnIndex;
            bcblColumns.SelectedIndexChanged += bcblColumns_SelectedIndexChanged;
            pgAdvanceColumnProperties.SelectedObject = propertiesObject;
        }

        /// <summary>
        /// Gets or sets the first item displayed in the advance column properties list
        /// </summary>
        public int AdvanceColumnTopIndex
        {
            get
            {
                return bcblColumns.TopIndex;
            }

            set
            {
                bcblColumns.TopIndex = value;
            }
        }

        #endregion

        #region Form event handlers

        /// <summary>
        /// User wants to accept the settings and close the component editor,
        /// fires LeavingTab, SavingSettings and SavedSettings events
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e"></param>
        private void btnOK_Click(object sender, EventArgs e)
        {
            CancelTabEventArgs eventArgs = new CancelTabEventArgs((EditorViewTab)tabControl.SelectedTab.Tag);

            if (LeavingTab != null)
                LeavingTab(this, eventArgs);

            if (eventArgs.Cancel)
                return;

            CancelEventArgs savingEventArgs = new CancelEventArgs();

            if (SavingSettings != null)
                SavingSettings(this, savingEventArgs);

            if (savingEventArgs.Cancel)
                return;

            if (SavedSettings != null)
                SavedSettings(this, new EventArgs());
        }

        /// <summary>
        /// Users wants to cancel any changes to settings and close the component,
        /// fires CancelSettings event
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (CancelSettings != null)
                CancelSettings(this, new EventArgs());
        }

        /// <summary>
        /// When the main flowLayout resizes ensures all contained components
        /// stretch to fit
        /// </summary>
        /// <param name="sender">The FlowLayout</param>
        /// <param name="e"></param>
        private void flowLayout_Resize(object sender, EventArgs e)
        {
            WidthResizeAllToFit();
            HeightResizeMainPanelToFit();
            HideTabButtonsAndResizeTabs();
            ReAssessErrorBoxLayout();
        }

        /// <summary>
        /// When the user selects an option from the treeview menu,
        /// fires LeavingTab and ViewSettings events and changes to the requested tab
        /// </summary>
        /// <param name="sender">The TreeView</param>
        /// <param name="e"></param>
        private void tvNavigation_BeforeSelect(object sender, TreeViewCancelEventArgs e)
        {
            if (LeavingTab != null)
            {
                CancelTabEventArgs eventArgs = new CancelTabEventArgs((EditorViewTab)tabControl.SelectedTab.Tag);
                LeavingTab(this, eventArgs);

                if (eventArgs.Cancel)
                {
                    e.Cancel = true;
                    return;
                }
            }

            switch ((EditorViewTab)e.Node.Tag)
            {
                case EditorViewTab.Columns:
                    tabControl.SelectedTab = tpColumns;
                    break;
                case EditorViewTab.Advance:
                    tabControl.SelectedTab = tpAdvance;
                    break;
                case EditorViewTab.Error:
                    tabControl.SelectedTab = tpErrorOutput;
                    break;
                case EditorViewTab.Auditing:
                    tabControl.SelectedTab = tpAuditing;
                    break;
                case EditorViewTab.Preview:
                    tabControl.SelectedTab = tpPreview;
                    break;
                default:
                    tabControl.SelectedTab = tpGeneral;
                    break;
            }

            if (ViewSettings != null)
                ViewSettings(this, new TabEventArgs((EditorViewTab)tabControl.SelectedTab.Tag));
        }

        /// <summary>
        /// User wants to add a new column configuration (advance tab)
        /// so this opens a dropdown menu displaying new column options
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e"></param>
        private void btnNewColumnMenu_Click(object sender, EventArgs e)
        {
            cmsNewColumnOption.Show(btnNewColumn, 0, btnNewColumnMenu.Height);
        }

        /// <summary>
        /// Enables/disables log output options
        /// </summary>
        /// <param name="sender">The CheckBox</param>
        /// <param name="e"></param>
        private void chkIncludeLogOutput_CheckedChanged(object sender, EventArgs e)
        {
            gbLogOutputOptions.Enabled = chkIncludeLogOutput.Checked;
        }

        /// <summary>
        /// Enables/disables error auditing options
        /// </summary>
        /// <param name="sender">The CheckBox</param>
        /// <param name="e"></param>
        private void chkIncludeErrorOutputAuditColumns_CheckedChanged(object sender, EventArgs e)
        {
            gbErrorOutputAuditColumns.Enabled = chkIncludeErrorOutputAuditColumns.Checked;
        }

        /// <summary>
        /// User has selected a different file connection manager name,
        /// fires the FileConnectionChange event
        /// </summary>
        /// <param name="sender">The ComboBox</param>
        /// <param name="e"></param>
        private void cmbConnectionManager_SelectedIndexChanged(object sender, EventArgs e)
        {
            // we have to get the connection manager combobox to validate so it will 
            // commit the pending selected item change to the underlying datasource,
            // this would otherwise only happen when the control loses focus...
            Validate();

            if (FileConnectionChange != null)
                FileConnectionChange(sender, e);
        }

        /// <summary>
        /// User has requested to reset column configurations based on current
        /// component settings, fires ResetColumns event
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e"></param>
        private void btnResetPreviewColumns_Click(object sender, EventArgs e)
        {
            if (ResetColumns != null)
                ResetColumns(this, new EventArgs());
        }

        /// <summary>
        /// User has updated the number of rows they'd like to skip in the preview
        /// and would like to update the rows displayed to reflect this, 
        /// fires the RefreshPreview event
        /// </summary>
        /// <param name="sender">The LinkLabel</param>
        /// <param name="e"></param>
        private void llRefreshPreviewRows_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (RefreshPreview != null)
                RefreshPreview(this, new EventArgs());
        }

        /// <summary>
        /// Forms has loaded, lets the initial tab know by firing the ViewSettings event
        /// </summary>
        /// <param name="sender">The EditorView</param>
        /// <param name="e"></param>
        private void EditorView_Load(object sender, EventArgs e)
        {
            if (ViewSettings != null)
                ViewSettings(this, new TabEventArgs((EditorViewTab)tabControl.SelectedTab.Tag));
        }

        /// <summary>
        /// User would like to insert a new column within the advance column configurations,
        /// fires the AddNewAdvanceColumn event
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e"></param>
        private void btnNewColumn_Click(object sender, EventArgs e)
        {
            if (AddNewAdvanceColumn != null)
                AddNewAdvanceColumn(this, new EventArgs());
        }

        /// <summary>
        /// User would like to insert a new column before the currently selected column
        /// within the advance column configuration, fires the AddNewAdvanceColumnBefore event
        /// </summary>
        /// <param name="sender">The ToolStripMenuItem</param>
        /// <param name="e"></param>
        private void miInsertColumnBefore_Click(object sender, EventArgs e)
        {
            if (AddNewAdvanceColumnBefore != null)
                AddNewAdvanceColumnBefore(this, new IndexEventArgs(bcblColumns.SelectedIndex));
        }

        /// <summary>
        /// User would like to insert a new column after the currently selected column
        /// within the advance column configuration, fires the AddNewAdvanceColumnAfter event
        /// </summary>
        /// <param name="sender">The ToolStripMenuItem</param>
        /// <param name="e"></param>
        private void miInsertColumnAfter_Click(object sender, EventArgs e)
        {
            if (AddNewAdvanceColumnAfter != null)
                AddNewAdvanceColumnAfter(this, new IndexEventArgs(bcblColumns.SelectedIndex));
        }

        /// <summary>
        /// User would like to delete the currently selected column within the advance
        /// column configuration, fires the RemoveAdvanceColumn event
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e"></param>
        private void btnDeleteColumn_Click(object sender, EventArgs e)
        {
            if (RemoveAdvanceColumn != null)
                RemoveAdvanceColumn(this, new IndexEventArgs(bcblColumns.SelectedIndex));
        }

        /// <summary>
        /// The user has selected a different column from the advance column configuration,
        /// fires the SelectedAdvanceColumnChanged event
        /// </summary>
        /// <param name="sender">The BindableCheckedListBox</param>
        /// <param name="e"></param>
        private void bcblColumns_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (SelectedAdvanceColumnChanged != null)
                SelectedAdvanceColumnChanged(this, new IndexEventArgs(bcblColumns.SelectedIndex));
        }
    
        /// <summary>
        /// Sets the selected gridview cells to the value specified 
        /// within the cmbErrorAction ComboBox
        /// </summary>
        /// <param name="sender">The Button</param>
        /// <param name="e"></param>
        private void btnApplyErrorAction_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewCell cell in dgvErrorOutput.SelectedCells)
            {
                if (!cell.ReadOnly)
                    cell.Value = cmbErrorAction.SelectedItem;
            }
        }

        /// <summary>
        /// Enables/disables the codepage combobox
        /// </summary>
        /// <param name="sender">The CheckBox</param>
        /// <param name="e"></param>
        private void chkIsUnicode_CheckedChanged(object sender, EventArgs e)
        {
            cmbCodePage.Enabled = !chkIsUnicode.Checked;
        }

        /// <summary>
        /// Disables the preview rows listview and allows the refresh
        /// preview link to be clicked
        /// </summary>
        /// <param name="sender">The NumericUpDown</param>
        /// <param name="e"></param>
        private void nudPreviewDataRowsToSkip_ValueChanged(object sender, EventArgs e)
        {
            lvPreviewRows.Enabled = false;
            llRefreshPreviewRows.Visible = true;
        }

        /// <summary>
        /// Enables/disables the log output datasource options
        /// </summary>
        /// <param name="sender">The CheckBox</param>
        /// <param name="e"></param>
        private void chkIncludeLogSourceData_CheckedChanged(object sender, EventArgs e)
        {
            cmbLogSourceDatatype.Enabled = chkIncludeLogSourceData.Checked;
            nudLogSourceSize.Enabled = chkIncludeLogSourceData.Checked;
        }

        /// <summary>
        /// Enables/disables the error output datasource options
        /// </summary>
        /// <param name="sender">The CheckBox</param>
        /// <param name="e"></param>
        private void chkIncludeErrorSourceData_CheckedChanged(object sender, EventArgs e)
        {
            cmbErrorSourceDataType.Enabled = chkIncludeErrorSourceData.Checked;
            nudErrorSourceSize.Enabled = chkIncludeErrorSourceData.Checked;
        }

        /// <summary>
        /// Handles all row/column delimiter combobox change events,
        /// fires the MainDelimitersChanged event
        /// </summary>
        /// <param name="sender">The ComboxBox</param>
        /// <param name="e"></param>
        private void MainDelimiter_TextChanged(object sender, EventArgs e)
        {
            Validate();

            if (MainDelimitersChanged != null)
                MainDelimitersChanged(sender, e);
        }

        /// <summary>
        /// Redirects event to MainDelimiter_TextChanged
        /// </summary>
        /// <param name="sender">The ComboBox</param>
        /// <param name="e"></param>
        private void cmbRowDelimiter_KeyUp(object sender, KeyEventArgs e)
        {
            MainDelimiter_TextChanged(sender, e);
        }

        /// <summary>
        /// Redirect event to MainDelimiter_TextChanged
        /// </summary>
        /// <param name="sender">The ComboBox</param>
        /// <param name="e"></param>
        private void cmbColumnDelimiter_KeyUp(object sender, KeyEventArgs e)
        {
            MainDelimiter_TextChanged(sender, e);
        }

        /// <summary>
        /// The user has changed the log output source datatype,
        /// fires the LogSourceDataTypeUpdated event
        /// </summary>
        /// <param name="sender">The ComboBox</param>
        /// <param name="e"></param>
        private void cmbLogSourceDatatype_SelectedIndexChanged(object sender, EventArgs e)
        {
            Validate();

            if (LogSourceDataTypeUpdated != null)
                LogSourceDataTypeUpdated(this, new EventArgs());
        }

        /// <summary>
        /// The user has changed the error output source datatype,
        /// fires the ErrorSourceDataTypeUpdated event
        /// </summary>
        /// <param name="sender">The ComboBox</param>
        /// <param name="e"></param>
        private void cmbErrorSourceDataType_SelectedIndexChanged(object sender, EventArgs e)
        {
            Validate();

            if (ErrorSourceDataTypeUpdated != null)
                ErrorSourceDataTypeUpdated(this, new EventArgs());
        }

        #endregion

        #region Form initialization and layout helper methods

        /// <summary>
        /// Associates each tabpage and tree node with the corresponding 
        /// SettingsTab value to allow for easy reference
        /// </summary>
        private void AssociateTabs()
        {
            foreach (TabPage tab in tabControl.TabPages)
            {
                tab.Tag = ConvertStringToSettingsTab(tab.Text);
            }

            foreach (TreeNode node in tvNavigation.Nodes)
            {
                node.Tag = ConvertStringToSettingsTab(node.Text);
            }
        }

        /// <summary>
        /// Takes a tab name and returns the corresponding SettingsTab value
        /// </summary>
        /// <param name="tab">The tab name to convert</param>
        /// <returns>The corresponding SettingsTab value</returns>
        private EditorViewTab ConvertStringToSettingsTab(string tab)
        {
            switch (tab)
            {
                case MessageStrings.ColumnsTabName:
                    return EditorViewTab.Columns;
                case MessageStrings.AdvanceTabName:
                    return EditorViewTab.Advance;
                case MessageStrings.ErrorOutputTabName:
                    return EditorViewTab.Error;
                case MessageStrings.AuditingTabName:
                    return EditorViewTab.Auditing;
                case MessageStrings.PreviewTabName:
                    return EditorViewTab.Preview;
                default:
                    return EditorViewTab.General;
            }
        }
      
        /// <summary>
        /// Repositions the tab control to account for no tabs
        /// and hides the tabs
        /// </summary>
        private void InitializeHiddenTabButtons()
        {
            tabControl.Top = tabControl.Top - tabControl.ItemSize.Height;
            HideTabButtonsAndResizeTabs();
        }

        /// <summary>
        /// Changes the tab control region to hide the tabs
        /// </summary>
        private void HideTabButtonsAndResizeTabs()
        {
            tabControl.Region =
                new Region(new RectangleF(tabControl.SelectedTab.Left, tabControl.SelectedTab.Top,
                                          tabControl.SelectedTab.Width,
                                          tabControl.SelectedTab.Height + tabControl.ItemSize.Height));
        }

        /// <summary>
        /// Saves certain control distances/measurements, that do not
        /// change with form resizing, for ease of future use
        /// </summary>
        private void SaveStaticFormMeasurements()
        {
            _flowLayoutWidthPadding = flowLayout.Width - pnlEditorButtons.Width;
            _errorBoxPanelHeight = pnlErrorBox.Height + pnlErrorBox.Margin.Top + pnlErrorBox.Margin.Bottom;
            _bottomPadding = flowLayout.Height - pnlEditorButtons.Top - pnlEditorButtons.Height -
                                pnlEditorButtons.Margin.Top - pnlEditorButtons.Margin.Bottom;
        }

        /// <summary>
        /// Changes the width of flow layout children to match 
        /// the flow layout
        /// </summary>
        /// <remarks>
        /// Because we cannot anchor controls to the flowlayout width
        /// we must manually update their width
        /// </remarks>
        private void WidthResizeAllToFit()
        {
            int controlWidth = flowLayout.Width - _flowLayoutWidthPadding;

            pnlErrorBox.Width = controlWidth;
            pnlEditorButtons.Width = controlWidth;
            pnlMain.Width = controlWidth;

            etchedLineBottom.Width = flowLayout.Width;
        }

        /// <summary>
        /// Based on if the error box is showing or not, this
        /// will fit the main panel to fill the available space
        /// </summary>
        private void HeightResizeMainPanelToFit()
        {
            int mainPanelHeight = flowLayout.Height - pnlEditorButtons.Height - pnlEditorButtons.Margin.Top
                - pnlEditorButtons.Margin.Bottom - pnlMain.Top - etchedLineBottom.Height - _bottomPadding;

            if (pnlErrorBox.Visible)
                mainPanelHeight -= _errorBoxPanelHeight;

            pnlMain.Height = mainPanelHeight;
        }

        /// <summary>
        /// Assesses if the form error box layout is suitable for
        /// the current form window state
        /// </summary>
        private void ReAssessErrorBoxLayout()
        {
            // nothing visible to assess
            if (!pnlErrorBox.Visible || this.WindowState == FormWindowState.Minimized)
                return;

            // no change in window state since last assessed
            if ((_formIsMaximized && this.WindowState == FormWindowState.Maximized) ||
                (!_formIsMaximized && this.WindowState == FormWindowState.Normal))
                return;

            _formIsMaximized = this.WindowState == FormWindowState.Maximized;

            if (_formIsMaximized)
                return;

            this.Height += _errorBoxPanelHeight;
        }

        #endregion

        #region Private tab settings and helper methods

        /// <summary>
        /// Populates a list view with a collection of rows under
        /// the provided column headers
        /// </summary>
        /// <param name="listView">The listview to populate</param>
        /// <param name="headers">The column headers to use</param>
        /// <param name="rows">The rows containing the column data to populate with</param>
        private void PopulateListView(ListView listView, List<string> headers, List<string[]> rows)
        {
            listView.Items.Clear();
            listView.Columns.Clear();

            if (headers.Count > 0)
            {
                foreach (string header in headers)
                {
                    ColumnHeader colHeader = new ColumnHeader()
                    {
                        Text = header,
                        Width = 100
                    };
                    listView.Columns.Add(colHeader);
                }
            }

            if (rows.Count > 0)
            {
                foreach (string[] row in rows)
                {
                    if (row.Length < 1)
                        continue;

                    ListViewItem firstColumn = new ListViewItem {Text = row[0]};

                    for (int fieldIndex = 1; fieldIndex < row.Length; fieldIndex++)
                    {
                        ListViewItem.ListViewSubItem otherColumns = new ListViewItem.ListViewSubItem();
                        otherColumns.Text = row[fieldIndex];
                        firstColumn.SubItems.Add(otherColumns);
                    }

                    listView.Items.Add(firstColumn);
                }
            }

            listView.ResumeLayout();
        }

        /// <summary>
        /// Toggles the visibility of the error message on the form
        /// </summary>
        /// <param name="showErrorMessage">True if the message should be visible</param>
        /// <param name="message">The error message to show</param>
        private void ToggleErrorMessage(bool showErrorMessage, string message)
        {
            if (!string.IsNullOrEmpty(message))
                txtErrorMessage.Text = message;

            if (pnlErrorBox.Visible == showErrorMessage && this.Created)
                return;

            pnlErrorBox.Visible = showErrorMessage;

            Control controlToResize = this;
            int resizeAmount = _errorBoxPanelHeight;

            if (this.WindowState == FormWindowState.Maximized)
            {
                controlToResize = pnlMain;
                resizeAmount = -_errorBoxPanelHeight;
            }

            if (showErrorMessage)
                controlToResize.Height += resizeAmount;
            else
                controlToResize.Height -= resizeAmount;
        }

        /// <summary>
        /// Clears all the bindings for controls on the general tab
        /// </summary>
        private void ClearGeneralTabBindings()
        {
            txtName.DataBindings.Clear();
            txtDescription.DataBindings.Clear();
            cmbConnectionManager.DataBindings.Clear();
            cmbLocale.DataBindings.Clear();
            cmbCodePage.DataBindings.Clear();
            chkIsUnicode.DataBindings.Clear();
            txtTextQualifier.DataBindings.Clear();
            cmbHeaderRowDelimiter.DataBindings.Clear();
            nudHeaderRowsToSkip.DataBindings.Clear();
            nudDataRowsToSkip.DataBindings.Clear();
            txtEscapeChar.DataBindings.Clear();
            chkColumnNamesAreInFirstDataRow.DataBindings.Clear();
            chkTrimWhitespace.DataBindings.Clear();
            chkTreatEmptyStringsAsNull.DataBindings.Clear();
        }

        /// <summary>
        /// Clears all the bindings for controls on the column tab
        /// </summary>
        private void ClearColumnTabBindings()
        {
            cmbRowDelimiter.DataBindings.Clear();
            cmbColumnDelimiter.DataBindings.Clear();
        }

        /// <summary>
        /// Clears all the bindings for controls on the error tab
        /// </summary>
        private void ClearErrorTabBindings()
        {
            cmbOutputTruncationAction.DataBindings.Clear();
            cmbOutputErrorAction.DataBindings.Clear();
            cmbErrorAction.DataBindings.Clear();
        }

        /// <summary>
        /// Clears all the bindings for controls on the audit tab
        /// </summary>
        private void ClearAuditTabBindings()
        {
            chkIncludeLogOutput.DataBindings.Clear();
            chkIncludeLogValidationMessages.DataBindings.Clear();
            chkIncludeLogSourceData.DataBindings.Clear();
            cmbLogSourceDatatype.DataBindings.Clear();
            nudLogSourceSize.DataBindings.Clear();

            chkIncludeErrorOutputAuditColumns.DataBindings.Clear();
            chkIncludeErrorValidationMessages.DataBindings.Clear();
            chkIncludeErrorSourceData.DataBindings.Clear();
            cmbErrorSourceDataType.DataBindings.Clear();
            nudErrorSourceSize.DataBindings.Clear();
        }

        /// <summary>
        /// Clears all the bindings for controls on the preview tab
        /// </summary>
        private void ClearPreviewTabBindings()
        {
            nudPreviewDataRowsToSkip.DataBindings.Clear();
        }

        #endregion
    }
}
